package com.yunqi.system.service;

import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.yunqi.starter.core.exception.BizException;
import com.yunqi.starter.core.lang.Lang;
import com.yunqi.starter.core.lang.Strings;
import com.yunqi.starter.core.lang.mvc.Mvcs;
import com.yunqi.starter.core.lang.util.NutMap;
import com.yunqi.starter.core.utils.IPUtil;
import com.yunqi.starter.security.spi.SecurityUtil;
import com.yunqi.starter.security.utils.SecuritySessionUtil;
import com.yunqi.system.models.SysAuthLog;
import com.yunqi.system.models.SysUser;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 权限认证
 * Created by @author CHQ on 2022/6/15
 */
@Service
public class ISysAuthService {

    @Resource
    ISysUserService sysUserService;

    @Resource
    ISysMenuService sysMenuService;

    @Resource
    ISysAuthLogService sysAuthLogService;


    /**
     * 账号密码登录
     * @param username      用户账号
     * @param password      用户密码
     * @return              当前会话的Token信息
     */
    public Map<String, String> login(String username, String password) {
        if(Strings.isBlank(username) || Strings.isBlank(password)){
            throw new BizException("缺少参数");
        }
        // 1、获取用户信息
        SysUser user = sysUserService.fetchByUsername(username);
        // 2、验证用户账号
        if (user == null) {
            throw new BizException("账号密码不正确");
        }
        if (!Strings.sNull(sysUserService.hashPassword(password, user.getSalt())).equalsIgnoreCase(user.getPassword())) {
            throw new BizException("账号密码不正确");
        }
        if (!user.isDisabled()) {
            throw new BizException("账号被禁用");
        }
        // 3、登录账号
        SecurityUtil.login(user.getId());
        // 4、检验当前会话是否已经登录，如未登录，则抛出异
        SecurityUtil.checkLogin();
        // 5、设置当前会话用户ID
        SecuritySessionUtil.setUserName(user.getUsername());
        // 6、设置当前会话用户名
        SecuritySessionUtil.setUserNickname(user.getNickname());
        // 7、记录并更新用户登录信息
        this.loginInfo(user);
        // 8、异步记录登录日志
        this.authLog("用户登录", "账号登录");
        // 9、返回当前会话的Token信息
        Map<String, String> map = new HashMap<>();
        map.put("token", SecurityUtil.getTokenValue());
        return map;
    }


    public List<NutMap> menus(){
        return sysMenuService.menus();
    }

    /**
     * 用户退出登录
     */
    @Transactional
    public void logout(){
        // 1、获取当前会话账号id
        String userId = SecuritySessionUtil.getUserId();
        // 2、记录用户退出信息
        SysUser user = sysUserService.fetch(userId);
        user.setOnline(false);
        sysUserService.updateIgnoreNull(user);
        // 3、异步记录登录日志
        this.authLog("用户登出", "退出系统");
        // 4、注销当前会话账号id
        SecurityUtil.logout(userId);
    }


    /**
     * 记录用户登录信息
     * @param user  用户信息
     */
    private void loginInfo(SysUser user){
        // *======== 记录用户登录信息 begin =========*
        HttpServletRequest req  = Mvcs.getReq();

        // 记录用户登录时间、累计登录次数、标记登录状态
        user.setLoginAt(System.currentTimeMillis());
        user.setLoginCount(user.getLoginCount() + 1);
        user.setOnline(true);

        // 获取操作地址&操作地点
        user.setLoginIp(Lang.getIP(req));
        user.setLoginLocation(IPUtil.getIPAddress(user.getLoginIp()));

        // 获取终端信息
        final UserAgent ua = UserAgentUtil.parse(req.getHeader("User-Agent"));

        // 获取客户端信息&操作系统信息
        user.setLoginBrowser(ua.getBrowser().getName() + "_" + ua.getVersion());
        user.setLoginOs(ua.getPlatform().getName() + "_"  + ua.getOsVersion());

        sysUserService.updateIgnoreNull(user);

        // *======== 记录用户登录信息 end =========*
    }

    /**
     * 记录登录日志
     * @param tag   日志标签
     * @param msg   日志内容
     */
    private void authLog(String tag, String msg){
        HttpServletRequest req  = Mvcs.getReq();
        // *========异步记录数据库日志 begin =========*
        SysAuthLog authLog = new SysAuthLog();
        authLog.setTag(tag);
        authLog.setMsg(msg);
        authLog.setName(SecuritySessionUtil.getUserNickname());
        authLog.setCreateUser(SecuritySessionUtil.getUserId());
        authLog.setUpdateUser(SecuritySessionUtil.getUserId());

        // 获取请求地址&请求方式
        authLog.setUrl(req.getRequestURI());
        authLog.setMethod(req.getMethod());

        // 获取操作地址&操作地点
        String ip = Lang.getIP(req);
        authLog.setIp(ip);
        authLog.setLocation(IPUtil.getIPAddress(ip));

        // 获取终端信息
        final UserAgent ua = UserAgentUtil.parse(req.getHeader("User-Agent"));

        // 获取客户端信息&操作系统信息
        authLog.setBrowser(ua.getBrowser().getName() + "_" + ua.getVersion());
        authLog.setOs(ua.getPlatform().getName() + "_"  + ua.getOsVersion());

        // 异步入库
        sysAuthLogService.asyncSaveLog(authLog);
        // *========异步记录数据库日志 end =========*
    }


    /**
     * 获取用户信息
     * @return  返回组装信息
     */
    public NutMap profile(){
        // 1、获取当前会话账号id
        SysUser user = sysUserService.fetch(SecuritySessionUtil.getUserId());
        // 2、返回组装信息
        NutMap data = Lang.obj2nutmap(user);
        data.put("avatar", user.getAvatar());
        data.put("menus", sysMenuService.all(user.getId()));
        data.put("nickname", user.getNickname());
        data.put("roles", sysUserService.getRoleList(user.getId()));

        NutMap map = new NutMap();
        map.put("user", data);
        return map;
    }

    /**
     * 修改密码
     * @param oldPwd    旧密码
     * @param newPwd    新密码
     */
    public void updatePassword(String oldPwd, String newPwd){
        if(Strings.isBlank(oldPwd) || Strings.isBlank(newPwd)){
            throw new BizException("缺少参数");
        }
        if(!(newPwd.length() >= 6 && newPwd.length()<= 20)){
            throw new BizException("新密码长度必须是6-20位");
        }
        // 获取当前登录的用户ID
        SysUser user = sysUserService.fetch(SecuritySessionUtil.getUserId());
        String old = sysUserService.hashPassword(oldPwd, user.getSalt());
        if(!old.equals(user.getPassword())){
            throw new BizException("密码不正确");
        }
        // 更新密码
        sysUserService.resetPassword(user.getId(), newPwd);
    }

}
